Need to install R >= 3.6 for ttBulk, tidyHeatmap. Also need to install tidyverse, devtools, edgeR.

Acknowledgements

This material is adapted from an R for RNA-seq workshop originally run here.

R for RNA-seq course

This course has been designed to introduce biologists to R for RNA-Seq analysis. The focus here is on using tidyverse to analyse RNA-seq data, as we believe this is a productive and engaging way to learn R for RNA-Seq analysis.

Intro to ttBulk package

In this course we will use the ttBulk package. ttBulk is a new package that provides a friendly tidyverse-style way to perform analysis of RNA-Seq data.

It will be added to Bioconductor (the home of many packages for the analysis of biological data) and when it is added there you can install it using the usual Bioconductor commands as described here.

In the meantime you can install ttBulk from it’s development site in Github with the command below. To run that command you will need the devtools package. If you don’t already have it installed install it with install.packages(“devtools”).

devtools::install_github("stemangiola/ttBulk@dev")

     

Set up working dir and/or use an RStudio project.

Workflow - test all commands will use first

Open a new script for this workshop File > New File > R Script. Save it as e.g. rna-seq.R.

Overview

TODO: Maybe add workflow image showing steps and functions used

  • Reading in tables of counts and metadata
  • Filtering and Normalisation of counts
  • Differential expression analysis
  • Visualisation (Heatmaps, MA plot, Volcano plot)

Introduction and data import

Measuring gene expression on a genome-wide scale has become common practice over the last two decades or so, with microarrays predominantly used pre-2008. With the advent of next generation sequencing technology in 2008, an increasing number of scientists use this technology to measure and understand changes in gene expression in often complex systems. As sequencing costs have decreased, using RNA-Seq to simultaneously measure the expression of tens of thousands of genes for multiple samples has never been easier. The cost of these experiments has now moved from generating the data to storing and analysing it.

There are many steps involved in analysing an RNA-Seq experiment. Analysing an RNAseq experiment begins with sequencing reads. These are aligned to a reference genome, then the number of reads mapped to each gene can be counted. This results in a table of counts, which is what we perform statistical analyses on in R. While mapping and counting are important and necessary tasks, today we will be starting from the count data and getting stuck into analysis.

First, let’s load all the packages we will need to analyse the data.

# load libraries
library(tidyverse)
library(ttBulk)

GREIN (GEO RNA-seq Experiments Interactive Navigator)

In this tutorial, we will learn some R through creating plots to visualise data from an RNA-seq experiment. RNA-seq counts file can be obtained from the GREIN platform. GREIN provides >6,500 published datasets from GEO that have been uniformly processed. It is available at http://www.ilincs.org/apps/grein/. You can search for a dataset of interest using the GEO code. We obtained the dataset used here using the code GSE60450. GREIN provide QC metrics for the RNA-seq datasets and both raw and normalized counts. We will use the raw counts here. Generally, the higher the number of counts the more the gene is expressed.

RNA-seq dataset

Here we will perform RNA-Seq analysis using data from the paper by Fu et al. 2015, GEO code GSE60450. This study examined expression in basal and luminal cells from mice at different stages (virgin, pregnant and lactating). There are 2 samples per group and 6 groups, 12 samples in total.

Reading in the data

Set up an RStudio project specifying the directory where you have saved the /data directory. Download and read in the data.

# read in counts file
counts <- read_csv("data/GSE60450_GeneLevel_Raw_data.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_character(),
  gene_symbol = col_character(),
  GSM1480291 = col_double(),
  GSM1480292 = col_double(),
  GSM1480293 = col_double(),
  GSM1480294 = col_double(),
  GSM1480295 = col_double(),
  GSM1480296 = col_double(),
  GSM1480297 = col_double(),
  GSM1480298 = col_double(),
  GSM1480299 = col_double(),
  GSM1480300 = col_double(),
  GSM1480301 = col_double(),
  GSM1480302 = col_double()
)
# read in metadata
sampleinfo <- read_csv("data/GSE60450_filtered_metadata.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_character(),
  characteristics = col_character(),
  immunophenotype = col_character(),
  `developmental stage` = col_character()
)

Let’s take a look at the data. You can type the name of the object to view the first few lines and to see how many rows and columns it has.

counts

The counts object contains information about genes (one gene per row), the first column has the Ensembl gene id, the second has the gene symbol and the remaining columns contain information about the number of reads aligning to the gene in each experimental sample. Note the gene counts here are not integers as they’re estimated counts from salmon (see here: https://support.bioconductor.org/p/101156/). There are two replicates for each cell type and time point (detailed sample info can be found in file “GSE60450_series_matrix.txt” from the GEO website). The sampleinfo metadata file contains basic information about the samples that we will need for the analysis today.

First we will convert the counts into long format (tidy format), similar to what we did in the Intro to R session.

# convert to tidy format
counts <- pivot_longer(counts, cols = starts_with("GSM"), names_to = "sample", values_to = "count") 
# take a look
counts

We will next extract just the columns we need, sample, gene_symbol, count. To do this we will use the tidyverse pipe %>%. This ‘pipes’ the output from the command on the left into the command on the right/below. Using the pipe is not essential but it reduces the amount of code we need to write when we have multiple steps (as we’ll see later). It also can make the steps clearer and easier to see. For more details on the pipe see here.

# using pipe
counts <- counts %>% 
  select(sample, gene_symbol, count, X1)
# take a look 
counts

Take a look at the sampleinfo file. The first column “X1” contains the sample ids, the second “characteristics” contains the specific group the sample belongs to (e.g. mammary gland, luminal cells, virgin), the third column “immunophenotype” contains just the cell type (luminal or basal) and the fourth column “developmental stage” contains just the stage (virgin, pregnant or lactating).

sampleinfo

We want to compare the groups in the “characteristics” column however the names are quite long so, similar to what we did in the Intro to R session, we’ll make a column containing shorter group names.

# make column called condition with shorter group names
sampleinfo <- mutate(sampleinfo, condition = case_when(                     str_detect(characteristics, "basal.*virgin") ~  "bvirg",
        str_detect(characteristics, "basal.*preg")  ~  "bpreg",
        str_detect(characteristics, "basal.*lact")  ~  "blact",
        str_detect(characteristics, "luminal.*virgin")  ~  "lvirg",
        str_detect(characteristics, "luminal.*preg")  ~  "lpreg",
        str_detect(characteristics, "luminal.*lact")  ~  "llact"
       ))
sampleinfo

Now we have our counts matrix in the long format we will join it to our sampleinfo so we have information on the samples, what groups they belong to. This is similar to what we did in the Intro to R session.

counts <- full_join(counts, sampleinfo, by = c("sample" = "X1"))
# take a look
counts

Now that we have our data in the format we want we will create a ttBulk object, that we can use to perform differential expression analysis with the ttBulk package. For this we need to specify our counts object and the names of the columns that contain our sample ids, our gene identifiers and our counts. Any other columns in the counts object e.g. our Ensembl gene id “X1” column will remain at the end.

#create a 'tt' object
counts <- ttBulk(counts, sample, gene_symbol, count)
# take a look
counts

Some gene symbols are not unique, they map to more than one gene id. We need to remove this redundancy and we can do that with ttBulk function aggregate_duplicates(). By default it will aggregate duplicate gene symbols summing their counts. Add way to identify what the duplicates are?

# get rid of duplicated gene symbols
counts <- aggregate_duplicates(counts)

We can plot the library sizes (number of reads) for the samples as a barplot to see whether there are any major discrepancies between the samples more easily.

# make barplot of counts
ggplot(data=counts, mapping=aes(x=sample, weight=count)) + geom_bar()

Filtering and Normalisation

Filtering lowly expressed genes

Genes with very low counts across all libraries provide little evidence for differential expression and they interfere with some of the statistical approximations that are used later in the pipeline. They also add to the multiple testing burden when estimating false discovery rates, reducing power to detect differentially expressed genes. These genes should be filtered out prior to further analysis.

There are a few ways to filter out lowly expressed genes. When there are biological replicates in each group, in this case we have a sample size of 2 in each group, we favour filtering on a minimum counts per million threshold present in at least 2 samples. Two represents the smallest sample size for each group in our experiment. In this dataset, we choose to retain genes if they are expressed at a counts-per-million (CPM) above 0.5 in at least two samples. A CPM of 0.5 is used as it corresponds to a count of 10-15 for the library sizes in this data set. If the count is any smaller, it is considered to be very low, indicating that the associated gene is not expressed in that sample. A requirement for expression in two or more libraries is used as each group contains two replicates. This ensures that a gene will be retained if it is only expressed in one group. Smaller CPM thresholds are usually appropriate for larger libraries. As a general rule, a good threshold can be chosen by identifying the CPM that corresponds to a count of 10, which in this case is about 0.5. You should filter with CPMs rather than filtering on the counts directly, as the latter does not account for differences in library sizes between samples.

Normalization for sequencing depth and composition

TMM normalization is performed to eliminate composition biases between libraries [@robinson2010tmm]. This generates a set of normalization factors, where the product of these factors and the library sizes defines the effective library size. TMM normalisation (and most scaling normalisation methods) scale relative to one sample.

In the ttBulk package the function scale_abundance() performs the filtering and normalisation to generate normalised counts.

# Normalisation for library size and composition bias (scale counts), 
counts.norm <- counts %>% scale_abundance()
# take a look
counts.norm

After we run scale_abundance() we should see some columns have been added to the end of counts. We have a column called filter out low counts that indicates whether the gene has been filtered due to being lowly expressed, FALSE means it wasn’t filtered, TRUE means it was. The count scaled column contains the scaled counts, after the normalisation has been applied.

We can create density plots to view the counts before and after filtering for low expression to see the effect of filtering. Note we need to take the log2 of the counts and add a small offset 1 to avoid taking log of zero.

# density plot before 
counts.norm %>% 
    ggplot(aes(log2(`count scaled` + 1), group=sample, color=condition)) +
    geom_density()

Exercise

Adapt the code above to create a density plot of the counts after filtering lowly expressed genes. How does it compare it to the density plot above?

# Solution
counts.norm %>% 
  filter(`filter out low counts` == FALSE) %>%
    ggplot(aes(log2(`count scaled`+1), color=condition)) +
    geom_density()

We can also create box plots to check the distributions of the counts in the samples. We can add a line through the median to help us see how similar (or not) the distributions are.

# box plot before scaling
counts.norm %>% 
  filter(`filter out low counts` == FALSE) %>%
    ggplot(aes(x=sample, y=log2(count+1), fill=condition)) +
    geom_boxplot() +
  geom_hline(aes(yintercept = median(log2(count+1)), colour = 'red'))

Exercise

Adapt the code above to create box plots that only includes the genes that have not been filtered due to low counts. How does it compare it to the box plots above?

# box plot after filtering
counts.norm %>% 
  filter(`filter out low counts` == FALSE) %>%
    ggplot(aes(x=sample, y=log2(`count scaled`+1), fill=condition)) +
    geom_boxplot() +
  geom_hline(aes(yintercept = median(log2(`count scaled`+1)), colour = 'red'))

Multidimensional scaling plots

By far, one of the most important plots we make when we analyse RNA-Seq data are MDS plots. An MDS plot is a visualisation of a principle components analysis, which determines the greatest sources of variation in the data. A principle components analysis is an example of an unsupervised analysis, where we don’t need to specify the groups. If your experiment is well controlled and has worked well, what we hope to see is that the greatest sources of variation in the data are the treatments/groups we are interested in. It is also an incredibly useful tool for quality control and checking for outliers. We can use the reduce_dimensions() function to calculate the dimensions.

# get MDS dimensions
counts.norm.MDS <-
  counts.norm %>%
  reduce_dimensions(method="MDS", .dims = 2)
# take a look
counts.norm.MDS

Then we can select just the dimensions for the samples.

# get the dimensions with all metadata
MDSdims <- counts.norm.MDS %>%
select(contains("Dim"), sample, immunophenotype, `developmental stage`, condition) %>%
distinct()
# take a look
MDSdims

Next we can plot the MDS dimensions as a scatterplot.

# MDS plot
ggplot(MDSdims, aes(x=Dim1, y=Dim2, colour=sample)) + 
  geom_point()

Exercise

Colour the MDS plot with different metadata variables e.g. immunophenotype. See what shape= does Discuss what is the greatest source of variation in the data (i.e. what does dimension 1 represent)? What is the second greatest source of variation in the data?

Solution

# MDS plot coloured by cell type
ggplot(MDSdims, aes(x=Dim1, y=Dim2, colour=condition)) + 
  geom_point()

# MDS plot coloured by cell type
ggplot(MDSdims, aes(x=Dim1, y=Dim2, colour=immunophenotype)) + 
  geom_point()

# MDS plot coloured by stage
ggplot(MDSdims, aes(x=Dim1, y=Dim2, colour=`developmental stage`)) + 
  geom_point()

# MDS plot coloured by stage with shape for cell type
ggplot(MDSdims, aes(x=Dim1, y=Dim2, colour=`developmental stage`, shape=immunophenotype)) + 
  geom_point()

Demo more MDS plots (sample swap, batch effects)

Differential expression

Now that we are happy that we have normalised the data and that the quality looks good, we can continue to testing for differentially expressed genes. We will use the test_differential_abundance() form ttBulk which uses edgeR to perform the differential expression analysis. We give test_differential_abundance() a formula (e.g. 0 + condition), specifying the column that contains our groups to be compared. We can also provide the names of the groups we want to compare/contrast to .contrasts (e.g. .contrasts = c(“conditionbpreg - conditionblact”))

# Differential expression with limma-voom (or edgeR)
counts.de <- counts %>%
    test_differential_abundance(~ 0 + condition,
                                .contrasts = c("conditionbpreg - conditionblact"))
ttBulk says: The design column names are "conditionblact, conditionbpreg, conditionbvirg, conditionllact, conditionlpreg, conditionlvirg" in case you are interested in contrasts
Joining, by = "gene_symbol"
Joining, by = "gene_symbol"
# take a look
counts.de

Now we have columns with our logFC and FDR P values.

Exercise

Perform differential expression for lpreg vs llact Perform for bpreg vs blact and lpreg vs llact at the same time

# maybe include
# remove condition from column names
# counts.de <- counts.de %>% 
#  rename_at(vars(contains("condition")), ~ str_replace(., "_condition", ""))

We can take a look at the top genes by P value. We can use filter to select only genes with FDR < 0.05, then use distinct to get the distincy values for columns of interest, and arrange to sort by PValue.

counts.de %>%
  filter(FDR < 0.05) %>%
  distinct(gene_symbol, logFC, PValue, FDR) %>%
  arrange(PValue)

We can write out our results to a file that can be loaded into Excel.

# save results
write_tsv(counts.de, "my_de_results.tsv")

Plots after testing for DE

Let’s make a few plots to make sure everything looks good and that we haven’t made a mistake in the analysis. Genome-wide plots that are useful for checking are MA plots and volcano plots. We can also use stripcharts and heatmaps to visualise groups of genes.

MA plots enable us to visualise amount of expression (logCPM) versus logFC. Highly expressed genes are towards the right of the plot. We can also colour significant genes (e.g. genes with FDR < 0.05)

# maplot, minimal
counts.de %>%
  filter(`filter out low counts` == FALSE) %>%
  ggplot(aes(x=logCPM, y=-logFC, colour=is_de)) +
  geom_point()

# colour by logfc & set up to red, down to blue

Volcano plots enable us to visualise significance of expression (logCPM) versus logFC. Highly significant genes are towards the top of the plot. We can also colour significant genes (e.g. genes with FDR < 0.05)

# volcanoplot, minimal
counts.de %>%
  filter(`filter out low counts` == FALSE) %>%
  ggplot(aes(x=logFC, y=-log10(PValue), colour=is_de)) +
  geom_point()
View(counts.de)

# colour by logfc & set up to red, down to blue, highlight some genes

Need to join the scaled counts to the de results to plot normalised counts per gene.

counts.de <- full_join(counts.de, counts.norm)
Joining, by = c("sample", "gene_symbol", "count", "X1", "characteristics", "immunophenotype", "developmental stage", "condition", "merged transcripts", "filter out low counts")
# stripcharts
counts.de %>%
    inner_join( (.) %>% distinct(gene_symbol, PValue) %>% arrange(PValue) %>% head(6)) %>%
    ggplot(aes(x = condition, y = log2(`count scaled` + 1), colour = condition)) +
    geom_jitter() +
    facet_wrap(~gene_symbol)
Joining, by = c("gene_symbol", "PValue")

# heatmap of DE genes
library(tidyHeatmap)
# basic
counts.de %>%
  filter(FDR < 0.05 & abs(logFC) > 4) %>%
  heatmap(
        .horizontal = sample,
        .vertical = gene_symbol,
        .abundance = `count scaled`,
        annotation = c(immunophenotype, `developmental stage`),
        log_transform = TRUE
    )

# customised
counts.de %>%
  filter(FDR < 0.05 & abs(logFC) > 4) %>%
  heatmap(
        .horizontal = sample,
        .vertical = gene_symbol,
        .abundance = `count scaled`,
        annotation = c(immunophenotype, `developmental stage`),
        log_transform = TRUE,
        palette_abundance = c("blue", "white", "red"),
        column_names_gp = gpar(fontsize = 8)
    )

LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFIgZm9yIFJOQS1zZXEiCmF1dGhvcjogIk1hcmlhIERveWxlIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDogeWVzCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCICVZJylgIgotLS0KTmVlZCB0byBpbnN0YWxsIFIgPj0gMy42IGZvciB0dEJ1bGssIHRpZHlIZWF0bWFwLiBBbHNvIG5lZWQgdG8gaW5zdGFsbCB0aWR5dmVyc2UsIGRldnRvb2xzLCBlZGdlUi4KCiMgQWNrbm93bGVkZ2VtZW50cwpUaGlzIG1hdGVyaWFsIGlzIGFkYXB0ZWQgZnJvbSBhbiBSIGZvciBSTkEtc2VxIHdvcmtzaG9wIG9yaWdpbmFsbHkgcnVuIFtoZXJlXShodHRwOi8vY29tYmluZS1hdXN0cmFsaWEuZ2l0aHViLmlvLzIwMTYtMDUtMTEtUk5Bc2VxLykuCiAgCiMgUiBmb3IgUk5BLXNlcSBjb3Vyc2UKICAKVGhpcyBjb3Vyc2UgaGFzIGJlZW4gZGVzaWduZWQgdG8gaW50cm9kdWNlIGJpb2xvZ2lzdHMgdG8gUiBmb3IgUk5BLVNlcSBhbmFseXNpcy4gVGhlIGZvY3VzIGhlcmUgaXMgb24gdXNpbmcgdGlkeXZlcnNlIHRvIGFuYWx5c2UgUk5BLXNlcSBkYXRhLCBhcyB3ZSBiZWxpZXZlIHRoaXMgaXMgYSBwcm9kdWN0aXZlIGFuZCBlbmdhZ2luZyB3YXkgdG8gbGVhcm4gUiBmb3IgUk5BLVNlcSBhbmFseXNpcy4KCiMgSW50cm8gdG8gdHRCdWxrIHBhY2thZ2UKCiMgIDxpbWcgc3JjPSJpbWFnZXMvdHRidWxrX2xvZ28ucG5nIiBoZWlnaHQ9IjEzOXB4IiB3aWR0aD0iMTIwcHgiIC8+CgpJbiB0aGlzIGNvdXJzZSB3ZSB3aWxsIHVzZSB0aGUgKip0dEJ1bGsqKiBwYWNrYWdlLiBbdHRCdWxrXShbaHR0cHM6Ly9naXRodWIuY29tL3N0ZW1hbmdpb2xhL3R0QnVsaykgaXMgYSBuZXcgcGFja2FnZSB0aGF0IHByb3ZpZGVzIGEgZnJpZW5kbHkgdGlkeXZlcnNlLXN0eWxlIHdheSB0byBwZXJmb3JtIGFuYWx5c2lzIG9mIFJOQS1TZXEgZGF0YS4KCkl0IHdpbGwgYmUgYWRkZWQgdG8gQmlvY29uZHVjdG9yICh0aGUgaG9tZSBvZiBtYW55IHBhY2thZ2VzIGZvciB0aGUgYW5hbHlzaXMgb2YgYmlvbG9naWNhbCBkYXRhKSBhbmQgd2hlbiBpdCBpcyBhZGRlZCB0aGVyZSB5b3UgY2FuIGluc3RhbGwgaXQgdXNpbmcgdGhlIHVzdWFsIEJpb2NvbmR1Y3RvciBjb21tYW5kcyBhcyBkZXNjcmliZWQgW2hlcmVdKGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvaW5zdGFsbC8pLgoKSW4gdGhlIG1lYW50aW1lIHlvdSBjYW4gaW5zdGFsbCB0dEJ1bGsgZnJvbSBpdCdzIGRldmVsb3BtZW50IHNpdGUgaW4gR2l0aHViIHdpdGggdGhlIGNvbW1hbmQgYmVsb3cuIFRvIHJ1biB0aGF0IGNvbW1hbmQgeW91IHdpbGwgbmVlZCB0aGUgZGV2dG9vbHMgcGFja2FnZS4gSWYgeW91IGRvbid0IGFscmVhZHkgaGF2ZSBpdCBpbnN0YWxsZWQgaW5zdGFsbCBpdCB3aXRoIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikuIAoKYGBge3IsIGV2YWw9RkFMU0V9CmRldnRvb2xzOjppbnN0YWxsX2dpdGh1Yigic3RlbWFuZ2lvbGEvdHRCdWxrQGRldiIpCmBgYAoKXCAgClwgIApcICAKClNldCB1cCB3b3JraW5nIGRpciBhbmQvb3IgdXNlIGFuIFJTdHVkaW8gcHJvamVjdC4KCldvcmtmbG93IC0gdGVzdCBhbGwgY29tbWFuZHMgd2lsbCB1c2UgZmlyc3QKCk9wZW4gYSBuZXcgc2NyaXB0IGZvciB0aGlzIHdvcmtzaG9wIEZpbGUgPiBOZXcgRmlsZSA+IFIgU2NyaXB0LiBTYXZlIGl0IGFzIGUuZy4gcm5hLXNlcS5SLgoKIyBPdmVydmlldwoKVE9ETzogTWF5YmUgYWRkIHdvcmtmbG93IGltYWdlIHNob3dpbmcgc3RlcHMgYW5kIGZ1bmN0aW9ucyB1c2VkCiAKKiBSZWFkaW5nIGluIHRhYmxlcyBvZiBjb3VudHMgYW5kIG1ldGFkYXRhCiogRmlsdGVyaW5nIGFuZCBOb3JtYWxpc2F0aW9uIG9mIGNvdW50cwoqIERpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzCiogVmlzdWFsaXNhdGlvbiAoSGVhdG1hcHMsIE1BIHBsb3QsIFZvbGNhbm8gcGxvdCkKCgojIEludHJvZHVjdGlvbiBhbmQgZGF0YSBpbXBvcnQKTWVhc3VyaW5nIGdlbmUgZXhwcmVzc2lvbiBvbiBhIGdlbm9tZS13aWRlIHNjYWxlIGhhcyBiZWNvbWUgY29tbW9uIHByYWN0aWNlIG92ZXIgdGhlIGxhc3QgdHdvIGRlY2FkZXMgb3Igc28sIHdpdGggbWljcm9hcnJheXMgcHJlZG9taW5hbnRseSB1c2VkIHByZS0yMDA4LiBXaXRoIHRoZSBhZHZlbnQgb2YgbmV4dCBnZW5lcmF0aW9uIHNlcXVlbmNpbmcgdGVjaG5vbG9neSBpbiAyMDA4LCBhbiBpbmNyZWFzaW5nIG51bWJlciBvZiBzY2llbnRpc3RzIHVzZSB0aGlzIHRlY2hub2xvZ3kgdG8gbWVhc3VyZSBhbmQgdW5kZXJzdGFuZCBjaGFuZ2VzIGluIGdlbmUgZXhwcmVzc2lvbiBpbiBvZnRlbiBjb21wbGV4IHN5c3RlbXMuIEFzIHNlcXVlbmNpbmcgY29zdHMgaGF2ZSBkZWNyZWFzZWQsIHVzaW5nIFJOQS1TZXEgdG8gc2ltdWx0YW5lb3VzbHkgbWVhc3VyZSB0aGUgZXhwcmVzc2lvbiBvZiB0ZW5zIG9mIHRob3VzYW5kcyBvZiBnZW5lcyBmb3IgbXVsdGlwbGUgc2FtcGxlcyBoYXMgbmV2ZXIgYmVlbiBlYXNpZXIuIFRoZSBjb3N0IG9mIHRoZXNlIGV4cGVyaW1lbnRzIGhhcyBub3cgbW92ZWQgZnJvbSBnZW5lcmF0aW5nIHRoZSBkYXRhIHRvIHN0b3JpbmcgYW5kIGFuYWx5c2luZyBpdC4KClRoZXJlIGFyZSBtYW55IHN0ZXBzIGludm9sdmVkIGluIGFuYWx5c2luZyBhbiBSTkEtU2VxIGV4cGVyaW1lbnQuIEFuYWx5c2luZyBhbiBSTkFzZXEgZXhwZXJpbWVudCBiZWdpbnMgd2l0aCBzZXF1ZW5jaW5nIHJlYWRzLiBUaGVzZSBhcmUgYWxpZ25lZCB0byBhIHJlZmVyZW5jZSBnZW5vbWUsIHRoZW4gdGhlIG51bWJlciBvZiByZWFkcyBtYXBwZWQgdG8gZWFjaCBnZW5lIGNhbiBiZSBjb3VudGVkLiBUaGlzIHJlc3VsdHMgaW4gYSB0YWJsZSBvZiBjb3VudHMsIHdoaWNoIGlzIHdoYXQgd2UgcGVyZm9ybSBzdGF0aXN0aWNhbCBhbmFseXNlcyBvbiBpbiBSLiBXaGlsZSBtYXBwaW5nIGFuZCBjb3VudGluZyBhcmUgaW1wb3J0YW50IGFuZCBuZWNlc3NhcnkgdGFza3MsIHRvZGF5IHdlIHdpbGwgYmUgc3RhcnRpbmcgZnJvbSB0aGUgY291bnQgZGF0YSBhbmQgZ2V0dGluZyBzdHVjayBpbnRvIGFuYWx5c2lzLgoKRmlyc3QsIGxldOKAmXMgbG9hZCBhbGwgdGhlIHBhY2thZ2VzIHdlIHdpbGwgbmVlZCB0byBhbmFseXNlIHRoZSBkYXRhLgoKYGBge3IgLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGxvYWQgbGlicmFyaWVzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHR0QnVsaykKYGBgCgojIyBHUkVJTiAoR0VPIFJOQS1zZXEgRXhwZXJpbWVudHMgSW50ZXJhY3RpdmUgTmF2aWdhdG9yKQpJbiB0aGlzIHR1dG9yaWFsLCB3ZSB3aWxsIGxlYXJuIHNvbWUgUiB0aHJvdWdoIGNyZWF0aW5nIHBsb3RzIHRvIHZpc3VhbGlzZSBkYXRhIGZyb20gYW4gUk5BLXNlcSBleHBlcmltZW50LiBSTkEtc2VxIGNvdW50cyBmaWxlIGNhbiBiZSBvYnRhaW5lZCBmcm9tIHRoZSBbR1JFSU4gcGxhdGZvcm1dKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTk4LTAxOS00MzkzNS04KS4gR1JFSU4gcHJvdmlkZXMgPjYsNTAwIHB1Ymxpc2hlZCBkYXRhc2V0cyBmcm9tIEdFTyB0aGF0IGhhdmUgYmVlbiB1bmlmb3JtbHkgcHJvY2Vzc2VkLiBJdCBpcyBhdmFpbGFibGUgYXQgaHR0cDovL3d3dy5pbGluY3Mub3JnL2FwcHMvZ3JlaW4vLiBZb3UgY2FuIHNlYXJjaCBmb3IgYSBkYXRhc2V0IG9mIGludGVyZXN0IHVzaW5nIHRoZSBHRU8gY29kZS4gV2Ugb2J0YWluZWQgdGhlIGRhdGFzZXQgdXNlZCBoZXJlIHVzaW5nIHRoZSBjb2RlIEdTRTYwNDUwLiBHUkVJTiBwcm92aWRlIFFDIG1ldHJpY3MgZm9yIHRoZSBSTkEtc2VxIGRhdGFzZXRzIGFuZCBib3RoIHJhdyBhbmQgbm9ybWFsaXplZCBjb3VudHMuIFdlIHdpbGwgdXNlIHRoZSByYXcgY291bnRzIGhlcmUuIEdlbmVyYWxseSwgdGhlIGhpZ2hlciB0aGUgbnVtYmVyIG9mIGNvdW50cyB0aGUgbW9yZSB0aGUgZ2VuZSBpcyBleHByZXNzZWQuCgojIyBSTkEtc2VxIGRhdGFzZXQKSGVyZSB3ZSB3aWxsIHBlcmZvcm0gUk5BLVNlcSBhbmFseXNpcyB1c2luZyBkYXRhIGZyb20gdGhlIHBhcGVyIGJ5IFtGdSBldCBhbC4gMjAxNV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wdWJtZWQvMjU3MzA0NzIpLCBHRU8gY29kZSBHU0U2MDQ1MC4gVGhpcyBzdHVkeSBleGFtaW5lZCBleHByZXNzaW9uIGluIGJhc2FsIGFuZCBsdW1pbmFsIGNlbGxzIGZyb20gbWljZSBhdCBkaWZmZXJlbnQgc3RhZ2VzICh2aXJnaW4sIHByZWduYW50IGFuZCBsYWN0YXRpbmcpLiBUaGVyZSBhcmUgMiBzYW1wbGVzIHBlciBncm91cCBhbmQgNiBncm91cHMsIDEyIHNhbXBsZXMgaW4gdG90YWwuCgohW10oaW1hZ2VzL21vdXNlX2V4cC5wbmcpCgojIyBSZWFkaW5nIGluIHRoZSBkYXRhCgoqU2V0IHVwIGFuIFJTdHVkaW8gcHJvamVjdCBzcGVjaWZ5aW5nIHRoZSBkaXJlY3Rvcnkgd2hlcmUgeW91IGhhdmUgc2F2ZWQgdGhlIGAvZGF0YWAgZGlyZWN0b3J5Ki4KRG93bmxvYWQgYW5kIHJlYWQgaW4gdGhlIGRhdGEuCgoKYGBge3J9CiMgcmVhZCBpbiBjb3VudHMgZmlsZQpjb3VudHMgPC0gcmVhZF9jc3YoImRhdGEvR1NFNjA0NTBfR2VuZUxldmVsX1Jhd19kYXRhLmNzdiIpCgojIHJlYWQgaW4gbWV0YWRhdGEKc2FtcGxlaW5mbyA8LSByZWFkX2NzdigiZGF0YS9HU0U2MDQ1MF9maWx0ZXJlZF9tZXRhZGF0YS5jc3YiKQpgYGAKCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBkYXRhLiBZb3UgY2FuIHR5cGUgdGhlIG5hbWUgb2YgdGhlIG9iamVjdCB0byB2aWV3IHRoZSBmaXJzdCBmZXcgbGluZXMgYW5kIHRvIHNlZSBob3cgbWFueSByb3dzIGFuZCBjb2x1bW5zIGl0IGhhcy4KCmBgYHtyfQpjb3VudHMKYGBgClRoZSBgY291bnRzYCBvYmplY3QgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgZ2VuZXMgKG9uZSBnZW5lIHBlciByb3cpLCB0aGUgZmlyc3QgY29sdW1uIGhhcyB0aGUgRW5zZW1ibCBnZW5lIGlkLCB0aGUgc2Vjb25kIGhhcyB0aGUgZ2VuZSBzeW1ib2wgYW5kIHRoZSByZW1haW5pbmcgY29sdW1ucyBjb250YWluIGluZm9ybWF0aW9uIGFib3V0IHRoZSBudW1iZXIgb2YgcmVhZHMgYWxpZ25pbmcgdG8gdGhlIGdlbmUgaW4gZWFjaCBleHBlcmltZW50YWwgc2FtcGxlLiBOb3RlIHRoZSBnZW5lIGNvdW50cyBoZXJlIGFyZSBub3QgaW50ZWdlcnMgYXMgdGhleSdyZSBlc3RpbWF0ZWQgY291bnRzIGZyb20gc2FsbW9uIChzZWUgaGVyZTogaHR0cHM6Ly9zdXBwb3J0LmJpb2NvbmR1Y3Rvci5vcmcvcC8xMDExNTYvKS4gVGhlcmUgYXJlIHR3byByZXBsaWNhdGVzIGZvciBlYWNoIGNlbGwgdHlwZSBhbmQgdGltZSBwb2ludCAoZGV0YWlsZWQgc2FtcGxlIGluZm8gY2FuIGJlIGZvdW5kIGluIGZpbGUgIkdTRTYwNDUwX3Nlcmllc19tYXRyaXgudHh0IiBmcm9tIHRoZSBbR0VPIHdlYnNpdGVdKGh0dHA6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vcXVlcnkvYWNjLmNnaT9hY2M9R1NFNjA0NTApKS4gVGhlIGBzYW1wbGVpbmZvYCBtZXRhZGF0YSBmaWxlIGNvbnRhaW5zIGJhc2ljIGluZm9ybWF0aW9uIGFib3V0IHRoZSBzYW1wbGVzIHRoYXQgd2Ugd2lsbCBuZWVkIGZvciB0aGUgYW5hbHlzaXMgdG9kYXkuCgpGaXJzdCB3ZSB3aWxsIGNvbnZlcnQgdGhlIGNvdW50cyBpbnRvIGxvbmcgZm9ybWF0ICh0aWR5IGZvcm1hdCksIHNpbWlsYXIgdG8gd2hhdCB3ZSBkaWQgaW4gdGhlIEludHJvIHRvIFIgc2Vzc2lvbi4KYGBge3J9CiMgY29udmVydCB0byB0aWR5IGZvcm1hdApjb3VudHMgPC0gcGl2b3RfbG9uZ2VyKGNvdW50cywgY29scyA9IHN0YXJ0c193aXRoKCJHU00iKSwgbmFtZXNfdG8gPSAic2FtcGxlIiwgdmFsdWVzX3RvID0gImNvdW50IikgCgojIHRha2UgYSBsb29rCmNvdW50cwpgYGAKCldlIHdpbGwgbmV4dCBleHRyYWN0IGp1c3QgdGhlIGNvbHVtbnMgd2UgbmVlZCwgc2FtcGxlLCBnZW5lX3N5bWJvbCwgY291bnQuIFRvIGRvIHRoaXMgd2Ugd2lsbCB1c2UgdGhlIHRpZHl2ZXJzZSBwaXBlIGAlPiVgLiBUaGlzICdwaXBlcycgdGhlIG91dHB1dCBmcm9tIHRoZSBjb21tYW5kIG9uIHRoZSBsZWZ0IGludG8gdGhlIGNvbW1hbmQgb24gdGhlIHJpZ2h0L2JlbG93LiBVc2luZyB0aGUgcGlwZSBpcyBub3QgZXNzZW50aWFsIGJ1dCBpdCByZWR1Y2VzIHRoZSBhbW91bnQgb2YgY29kZSB3ZSBuZWVkIHRvIHdyaXRlIHdoZW4gd2UgaGF2ZSBtdWx0aXBsZSBzdGVwcyAoYXMgd2UnbGwgc2VlIGxhdGVyKS4gSXQgYWxzbyBjYW4gbWFrZSB0aGUgc3RlcHMgY2xlYXJlciBhbmQgZWFzaWVyIHRvIHNlZS4gIEZvciBtb3JlIGRldGFpbHMgb24gdGhlIHBpcGUgc2VlIFtoZXJlXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3BpcGVzLmh0bWwpLgoKYGBge3J9CiMgdXNpbmcgcGlwZQpjb3VudHMgPC0gY291bnRzICU+JSAKICBzZWxlY3Qoc2FtcGxlLCBnZW5lX3N5bWJvbCwgY291bnQsIFgxKQoKIyB0YWtlIGEgbG9vayAKY291bnRzCmBgYAoKVGFrZSBhIGxvb2sgYXQgdGhlIHNhbXBsZWluZm8gZmlsZS4gVGhlIGZpcnN0IGNvbHVtbiAiWDEiIGNvbnRhaW5zIHRoZSBzYW1wbGUgaWRzLCB0aGUgc2Vjb25kICJjaGFyYWN0ZXJpc3RpY3MiIGNvbnRhaW5zIHRoZSBzcGVjaWZpYyBncm91cCB0aGUgc2FtcGxlIGJlbG9uZ3MgdG8gKGUuZy4gbWFtbWFyeSBnbGFuZCwgbHVtaW5hbCBjZWxscywgdmlyZ2luKSwgdGhlIHRoaXJkIGNvbHVtbiAiaW1tdW5vcGhlbm90eXBlIiBjb250YWlucyBqdXN0IHRoZSBjZWxsIHR5cGUgKGx1bWluYWwgb3IgYmFzYWwpIGFuZCB0aGUgZm91cnRoIGNvbHVtbiAiZGV2ZWxvcG1lbnRhbCBzdGFnZSIgY29udGFpbnMganVzdCB0aGUgc3RhZ2UgKHZpcmdpbiwgcHJlZ25hbnQgb3IgbGFjdGF0aW5nKS4KCmBgYHtyfQpzYW1wbGVpbmZvCmBgYAoKV2Ugd2FudCB0byBjb21wYXJlIHRoZSBncm91cHMgaW4gdGhlICJjaGFyYWN0ZXJpc3RpY3MiIGNvbHVtbiBob3dldmVyIHRoZSBuYW1lcyBhcmUgcXVpdGUgbG9uZyBzbywgc2ltaWxhciB0byB3aGF0IHdlIGRpZCBpbiB0aGUgSW50cm8gdG8gUiBzZXNzaW9uLCB3ZSdsbCBtYWtlIGEgY29sdW1uIGNvbnRhaW5pbmcgc2hvcnRlciBncm91cCBuYW1lcy4KCgpgYGB7cn0KIyBtYWtlIGNvbHVtbiBjYWxsZWQgY29uZGl0aW9uIHdpdGggc2hvcnRlciBncm91cCBuYW1lcwpzYW1wbGVpbmZvIDwtIG11dGF0ZShzYW1wbGVpbmZvLCBjb25kaXRpb24gPSBjYXNlX3doZW4oICAgICAgICAgICAgICAgICAgICAgc3RyX2RldGVjdChjaGFyYWN0ZXJpc3RpY3MsICJiYXNhbC4qdmlyZ2luIikgfiAgImJ2aXJnIiwKICAgICAgICBzdHJfZGV0ZWN0KGNoYXJhY3RlcmlzdGljcywgImJhc2FsLipwcmVnIikgIH4gICJicHJlZyIsCiAgICAgICAgc3RyX2RldGVjdChjaGFyYWN0ZXJpc3RpY3MsICJiYXNhbC4qbGFjdCIpICB+ICAiYmxhY3QiLAogICAgICAgIHN0cl9kZXRlY3QoY2hhcmFjdGVyaXN0aWNzLCAibHVtaW5hbC4qdmlyZ2luIikgIH4gICJsdmlyZyIsCiAgICAgICAgc3RyX2RldGVjdChjaGFyYWN0ZXJpc3RpY3MsICJsdW1pbmFsLipwcmVnIikgIH4gICJscHJlZyIsCiAgICAgICAgc3RyX2RldGVjdChjaGFyYWN0ZXJpc3RpY3MsICJsdW1pbmFsLipsYWN0IikgIH4gICJsbGFjdCIKICAgICAgICkpCgpzYW1wbGVpbmZvCmBgYAoKTm93IHdlIGhhdmUgb3VyIGNvdW50cyBtYXRyaXggaW4gdGhlIGxvbmcgZm9ybWF0IHdlIHdpbGwgam9pbiBpdCB0byBvdXIgc2FtcGxlaW5mbyBzbyB3ZSBoYXZlIGluZm9ybWF0aW9uIG9uIHRoZSBzYW1wbGVzLCB3aGF0IGdyb3VwcyB0aGV5IGJlbG9uZyB0by4gVGhpcyBpcyBzaW1pbGFyIHRvIHdoYXQgd2UgZGlkIGluIHRoZSBJbnRybyB0byBSIHNlc3Npb24uCgpgYGB7cn0KY291bnRzIDwtIGZ1bGxfam9pbihjb3VudHMsIHNhbXBsZWluZm8sIGJ5ID0gYygic2FtcGxlIiA9ICJYMSIpKQoKIyB0YWtlIGEgbG9vawpjb3VudHMKYGBgCgpOb3cgdGhhdCB3ZSBoYXZlIG91ciBkYXRhIGluIHRoZSBmb3JtYXQgd2Ugd2FudCB3ZSB3aWxsIGNyZWF0ZSBhIHR0QnVsayBvYmplY3QsIHRoYXQgd2UgY2FuIHVzZSB0byBwZXJmb3JtIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHdpdGggdGhlIHR0QnVsayBwYWNrYWdlLiBGb3IgdGhpcyB3ZSBuZWVkIHRvIHNwZWNpZnkgb3VyIGNvdW50cyBvYmplY3QgYW5kIHRoZSBuYW1lcyBvZiB0aGUgY29sdW1ucyB0aGF0IGNvbnRhaW4gb3VyIHNhbXBsZSBpZHMsIG91ciBnZW5lIGlkZW50aWZpZXJzIGFuZCBvdXIgY291bnRzLiBBbnkgb3RoZXIgY29sdW1ucyBpbiB0aGUgY291bnRzIG9iamVjdCBlLmcuIG91ciBFbnNlbWJsIGdlbmUgaWQgIlgxIiBjb2x1bW4gd2lsbCByZW1haW4gYXQgdGhlIGVuZC4KYGBge3J9CiNjcmVhdGUgYSAndHQnIG9iamVjdApjb3VudHMgPC0gdHRCdWxrKGNvdW50cywgc2FtcGxlLCBnZW5lX3N5bWJvbCwgY291bnQpCgojIHRha2UgYSBsb29rCmNvdW50cwpgYGAKU29tZSBnZW5lIHN5bWJvbHMgYXJlIG5vdCB1bmlxdWUsIHRoZXkgbWFwIHRvIG1vcmUgdGhhbiBvbmUgZ2VuZSBpZC4gV2UgbmVlZCB0byByZW1vdmUgdGhpcyByZWR1bmRhbmN5ICBhbmQgd2UgY2FuIGRvIHRoYXQgd2l0aCB0dEJ1bGsgZnVuY3Rpb24gYWdncmVnYXRlX2R1cGxpY2F0ZXMoKS4gQnkgZGVmYXVsdCBpdCB3aWxsIGFnZ3JlZ2F0ZSBkdXBsaWNhdGUgZ2VuZSBzeW1ib2xzIHN1bW1pbmcgdGhlaXIgY291bnRzLiBBZGQgd2F5IHRvIGlkZW50aWZ5IHdoYXQgdGhlIGR1cGxpY2F0ZXMgYXJlPyAgCgpgYGB7cn0KIyBnZXQgcmlkIG9mIGR1cGxpY2F0ZWQgZ2VuZSBzeW1ib2xzCmNvdW50cyA8LSBhZ2dyZWdhdGVfZHVwbGljYXRlcyhjb3VudHMpCmBgYAoKV2UgY2FuIHBsb3QgdGhlIGxpYnJhcnkgc2l6ZXMgKG51bWJlciBvZiByZWFkcykgZm9yIHRoZSBzYW1wbGVzIGFzIGEgYmFycGxvdCB0byBzZWUgd2hldGhlciB0aGVyZSBhcmUgYW55IG1ham9yIGRpc2NyZXBhbmNpZXMgYmV0d2VlbiB0aGUgc2FtcGxlcyBtb3JlIGVhc2lseS4KCmBgYHtyfQojIG1ha2UgYmFycGxvdCBvZiBjb3VudHMKZ2dwbG90KGRhdGE9Y291bnRzLCBtYXBwaW5nPWFlcyh4PXNhbXBsZSwgd2VpZ2h0PWNvdW50KSkgKyBnZW9tX2JhcigpCmBgYAojIEZpbHRlcmluZyBhbmQgTm9ybWFsaXNhdGlvbgoKIyMjIEZpbHRlcmluZyBsb3dseSBleHByZXNzZWQgZ2VuZXMgIApHZW5lcyB3aXRoIHZlcnkgbG93IGNvdW50cyBhY3Jvc3MgYWxsIGxpYnJhcmllcyBwcm92aWRlIGxpdHRsZSBldmlkZW5jZSBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5kIHRoZXkgaW50ZXJmZXJlIHdpdGggc29tZSBvZiB0aGUgc3RhdGlzdGljYWwgYXBwcm94aW1hdGlvbnMgdGhhdCBhcmUgdXNlZCBsYXRlciBpbiB0aGUgcGlwZWxpbmUuIFRoZXkgYWxzbyBhZGQgdG8gdGhlIG11bHRpcGxlIHRlc3RpbmcgYnVyZGVuIHdoZW4gZXN0aW1hdGluZyBmYWxzZSBkaXNjb3ZlcnkgcmF0ZXMsIHJlZHVjaW5nIHBvd2VyIHRvIGRldGVjdCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIFRoZXNlIGdlbmVzIHNob3VsZCBiZSBmaWx0ZXJlZCBvdXQgcHJpb3IgdG8gZnVydGhlciBhbmFseXNpcy4KClRoZXJlIGFyZSBhIGZldyB3YXlzIHRvIGZpbHRlciBvdXQgbG93bHkgZXhwcmVzc2VkIGdlbmVzLiBXaGVuIHRoZXJlIGFyZSBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMgaW4gZWFjaCBncm91cCwgaW4gdGhpcyBjYXNlIHdlIGhhdmUgYSBzYW1wbGUgc2l6ZSBvZiAyIGluIGVhY2ggZ3JvdXAsIHdlIGZhdm91ciBmaWx0ZXJpbmcgb24gYSBtaW5pbXVtIGNvdW50cyBwZXIgbWlsbGlvbiB0aHJlc2hvbGQgcHJlc2VudCBpbiBhdCBsZWFzdCAyIHNhbXBsZXMuIFR3byByZXByZXNlbnRzIHRoZSBzbWFsbGVzdCBzYW1wbGUgc2l6ZSBmb3IgZWFjaCBncm91cCBpbiBvdXIgZXhwZXJpbWVudC4gSW4gdGhpcyBkYXRhc2V0LCB3ZSBjaG9vc2UgdG8gcmV0YWluIGdlbmVzIGlmIHRoZXkgYXJlIGV4cHJlc3NlZCBhdCBhIGNvdW50cy1wZXItbWlsbGlvbiAoQ1BNKSBhYm92ZSAwLjUgaW4gYXQgbGVhc3QgdHdvIHNhbXBsZXMuIEEgQ1BNIG9mIDAuNSBpcyB1c2VkIGFzIGl0IGNvcnJlc3BvbmRzIHRvIGEgY291bnQgb2YgMTAtMTUgZm9yIHRoZSBsaWJyYXJ5IHNpemVzIGluIHRoaXMgZGF0YSBzZXQuIElmIHRoZSBjb3VudCBpcyBhbnkgc21hbGxlciwgaXQgaXMgY29uc2lkZXJlZCB0byBiZSB2ZXJ5IGxvdywgaW5kaWNhdGluZyB0aGF0IHRoZSBhc3NvY2lhdGVkIGdlbmUgaXMgbm90IGV4cHJlc3NlZCBpbiB0aGF0IHNhbXBsZS4gQSByZXF1aXJlbWVudCBmb3IgZXhwcmVzc2lvbiBpbiB0d28gb3IgbW9yZSBsaWJyYXJpZXMgaXMgdXNlZCBhcyBlYWNoIGdyb3VwIGNvbnRhaW5zIHR3byByZXBsaWNhdGVzLiBUaGlzIGVuc3VyZXMgdGhhdCBhIGdlbmUgd2lsbCBiZSByZXRhaW5lZCBpZiBpdCBpcyBvbmx5IGV4cHJlc3NlZCBpbiBvbmUgZ3JvdXAuIFNtYWxsZXIgQ1BNIHRocmVzaG9sZHMgYXJlIHVzdWFsbHkgYXBwcm9wcmlhdGUgZm9yIGxhcmdlciBsaWJyYXJpZXMuIEFzIGEgZ2VuZXJhbCBydWxlLCBhIGdvb2QgdGhyZXNob2xkIGNhbiBiZSBjaG9zZW4gYnkgaWRlbnRpZnlpbmcgdGhlIENQTSB0aGF0IGNvcnJlc3BvbmRzIHRvIGEgY291bnQgb2YgMTAsIHdoaWNoIGluIHRoaXMgY2FzZSBpcyBhYm91dCAwLjUuIFlvdSBzaG91bGQgZmlsdGVyIHdpdGggQ1BNcyByYXRoZXIgdGhhbiBmaWx0ZXJpbmcgb24gdGhlIGNvdW50cyBkaXJlY3RseSwgYXMgdGhlIGxhdHRlciBkb2VzIG5vdCBhY2NvdW50IGZvciBkaWZmZXJlbmNlcyBpbiBsaWJyYXJ5IHNpemVzIGJldHdlZW4gc2FtcGxlcy4KCiMjIyBOb3JtYWxpemF0aW9uIGZvciBzZXF1ZW5jaW5nIGRlcHRoIGFuZCBjb21wb3NpdGlvbgoKVE1NIG5vcm1hbGl6YXRpb24gaXMgcGVyZm9ybWVkIHRvIGVsaW1pbmF0ZSBjb21wb3NpdGlvbiBiaWFzZXMgYmV0d2VlbiBsaWJyYXJpZXMgW0Byb2JpbnNvbjIwMTB0bW1dLiBUaGlzIGdlbmVyYXRlcyBhIHNldCBvZiBub3JtYWxpemF0aW9uIGZhY3RvcnMsIHdoZXJlIHRoZSBwcm9kdWN0IG9mIHRoZXNlIGZhY3RvcnMgYW5kIHRoZSBsaWJyYXJ5IHNpemVzIGRlZmluZXMgdGhlIGVmZmVjdGl2ZSBsaWJyYXJ5IHNpemUuIFRNTSBub3JtYWxpc2F0aW9uIChhbmQgbW9zdCBzY2FsaW5nIG5vcm1hbGlzYXRpb24gbWV0aG9kcykgc2NhbGUgcmVsYXRpdmUgdG8gb25lIHNhbXBsZS4KCkluIHRoZSB0dEJ1bGsgcGFja2FnZSB0aGUgZnVuY3Rpb24gc2NhbGVfYWJ1bmRhbmNlKCkgcGVyZm9ybXMgdGhlIGZpbHRlcmluZyBhbmQgbm9ybWFsaXNhdGlvbiB0byBnZW5lcmF0ZSBub3JtYWxpc2VkIGNvdW50cy4KCmBgYHtyfQojIE5vcm1hbGlzYXRpb24gZm9yIGxpYnJhcnkgc2l6ZSBhbmQgY29tcG9zaXRpb24gYmlhcyAoc2NhbGUgY291bnRzKSwgCmNvdW50cy5ub3JtIDwtIGNvdW50cyAlPiUgc2NhbGVfYWJ1bmRhbmNlKCkKCiMgdGFrZSBhIGxvb2sKY291bnRzLm5vcm0KYGBgCgpBZnRlciB3ZSBydW4gc2NhbGVfYWJ1bmRhbmNlKCkgd2Ugc2hvdWxkIHNlZSBzb21lIGNvbHVtbnMgaGF2ZSBiZWVuIGFkZGVkIHRvIHRoZSBlbmQgb2YgY291bnRzLiBXZSBoYXZlIGEgY29sdW1uIGNhbGxlZCBgZmlsdGVyIG91dCBsb3cgY291bnRzYCB0aGF0IGluZGljYXRlcyB3aGV0aGVyIHRoZSBnZW5lIGhhcyBiZWVuIGZpbHRlcmVkIGR1ZSB0byBiZWluZyBsb3dseSBleHByZXNzZWQsIEZBTFNFIG1lYW5zIGl0IHdhc24ndCBmaWx0ZXJlZCwgVFJVRSBtZWFucyBpdCB3YXMuIFRoZSBgY291bnQgc2NhbGVkYCBjb2x1bW4gY29udGFpbnMgdGhlIHNjYWxlZCBjb3VudHMsIGFmdGVyIHRoZSBub3JtYWxpc2F0aW9uIGhhcyBiZWVuIGFwcGxpZWQuCgpXZSBjYW4gY3JlYXRlIGRlbnNpdHkgcGxvdHMgdG8gdmlldyB0aGUgY291bnRzIGJlZm9yZSBhbmQgYWZ0ZXIgZmlsdGVyaW5nIGZvciBsb3cgZXhwcmVzc2lvbiB0byBzZWUgdGhlIGVmZmVjdCBvZiBmaWx0ZXJpbmcuIE5vdGUgd2UgbmVlZCB0byB0YWtlIHRoZSBsb2cyIG9mIHRoZSBjb3VudHMgYW5kIGFkZCBhIHNtYWxsIG9mZnNldCAxIHRvIGF2b2lkIHRha2luZyBsb2cgb2YgemVyby4KCmBgYHtyfQojIGRlbnNpdHkgcGxvdCBiZWZvcmUgCmNvdW50cy5ub3JtICU+JSAKICAgIGdncGxvdChhZXMobG9nMihgY291bnQgc2NhbGVkYCArIDEpLCBncm91cD1zYW1wbGUsIGNvbG9yPWNvbmRpdGlvbikpICsKICAgIGdlb21fZGVuc2l0eSgpCmBgYAoKIyMjIyBFeGVyY2lzZQpBZGFwdCB0aGUgY29kZSBhYm92ZSB0byBjcmVhdGUgYSBkZW5zaXR5IHBsb3Qgb2YgdGhlIGNvdW50cyBhZnRlciBmaWx0ZXJpbmcgbG93bHkgZXhwcmVzc2VkIGdlbmVzLiBIb3cgZG9lcyBpdCBjb21wYXJlIGl0IHRvIHRoZSBkZW5zaXR5IHBsb3QgYWJvdmU/CgpgYGB7cn0KIyBTb2x1dGlvbgpjb3VudHMubm9ybSAlPiUgCiAgZmlsdGVyKGBmaWx0ZXIgb3V0IGxvdyBjb3VudHNgID09IEZBTFNFKSAlPiUKICAgIGdncGxvdChhZXMobG9nMihgY291bnQgc2NhbGVkYCsxKSwgY29sb3I9Y29uZGl0aW9uKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkKYGBgCgpXZSBjYW4gYWxzbyBjcmVhdGUgYm94IHBsb3RzIHRvIGNoZWNrIHRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSBjb3VudHMgaW4gdGhlIHNhbXBsZXMuIFdlIGNhbiBhZGQgYSBsaW5lIHRocm91Z2ggdGhlIG1lZGlhbiB0byBoZWxwIHVzIHNlZSBob3cgc2ltaWxhciAob3Igbm90KSB0aGUgZGlzdHJpYnV0aW9ucyBhcmUuCgpgYGB7cn0KIyBib3ggcGxvdCBiZWZvcmUgc2NhbGluZwpjb3VudHMubm9ybSAlPiUgCiAgZmlsdGVyKGBmaWx0ZXIgb3V0IGxvdyBjb3VudHNgID09IEZBTFNFKSAlPiUKICAgIGdncGxvdChhZXMoeD1zYW1wbGUsIHk9bG9nMihjb3VudCsxKSwgZmlsbD1jb25kaXRpb24pKSArCiAgICBnZW9tX2JveHBsb3QoKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1lZGlhbihsb2cyKGNvdW50KzEpKSwgY29sb3VyID0gJ3JlZCcpKQpgYGAKCiMjIyMgRXhlcmNpc2UKQWRhcHQgdGhlIGNvZGUgYWJvdmUgdG8gY3JlYXRlIGJveCBwbG90cyB0aGF0IG9ubHkgaW5jbHVkZXMgdGhlIGdlbmVzIHRoYXQgaGF2ZSBub3QgYmVlbiBmaWx0ZXJlZCBkdWUgdG8gbG93IGNvdW50cy4gSG93IGRvZXMgaXQgY29tcGFyZSBpdCB0byB0aGUgYm94IHBsb3RzIGFib3ZlPwoKYGBge3J9CiMgYm94IHBsb3QgYWZ0ZXIgZmlsdGVyaW5nCmNvdW50cy5ub3JtICU+JSAKICBmaWx0ZXIoYGZpbHRlciBvdXQgbG93IGNvdW50c2AgPT0gRkFMU0UpICU+JQogICAgZ2dwbG90KGFlcyh4PXNhbXBsZSwgeT1sb2cyKGBjb3VudCBzY2FsZWRgKzEpLCBmaWxsPWNvbmRpdGlvbikpICsKICAgIGdlb21fYm94cGxvdCgpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWVkaWFuKGxvZzIoYGNvdW50IHNjYWxlZGArMSkpLCBjb2xvdXIgPSAncmVkJykpCmBgYAoKCiMgTXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIHBsb3RzCgpCeSBmYXIsIG9uZSBvZiB0aGUgbW9zdCBpbXBvcnRhbnQgcGxvdHMgd2UgbWFrZSB3aGVuIHdlIGFuYWx5c2UgUk5BLVNlcSBkYXRhIGFyZSBNRFMgcGxvdHMuIEFuIE1EUyBwbG90IGlzIGEgdmlzdWFsaXNhdGlvbiBvZiBhIHByaW5jaXBsZSBjb21wb25lbnRzIGFuYWx5c2lzLCB3aGljaCBkZXRlcm1pbmVzIHRoZSBncmVhdGVzdCBzb3VyY2VzIG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YS4gQSBwcmluY2lwbGUgY29tcG9uZW50cyBhbmFseXNpcyBpcyBhbiBleGFtcGxlIG9mIGFuIHVuc3VwZXJ2aXNlZCBhbmFseXNpcywgd2hlcmUgd2UgZG9uJ3QgbmVlZCB0byBzcGVjaWZ5IHRoZSBncm91cHMuIElmIHlvdXIgZXhwZXJpbWVudCBpcyB3ZWxsIGNvbnRyb2xsZWQgYW5kIGhhcyB3b3JrZWQgd2VsbCwgd2hhdCB3ZSBob3BlIHRvIHNlZSBpcyB0aGF0IHRoZSBncmVhdGVzdCBzb3VyY2VzIG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YSBhcmUgdGhlIHRyZWF0bWVudHMvZ3JvdXBzIHdlIGFyZSBpbnRlcmVzdGVkIGluLiBJdCBpcyBhbHNvIGFuIGluY3JlZGlibHkgdXNlZnVsIHRvb2wgZm9yIHF1YWxpdHkgY29udHJvbCBhbmQgY2hlY2tpbmcgZm9yIG91dGxpZXJzLiBXZSBjYW4gdXNlIHRoZSBgcmVkdWNlX2RpbWVuc2lvbnMoKWAgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBkaW1lbnNpb25zLgoKCmBgYHtyfQojIGdldCBNRFMgZGltZW5zaW9ucwpjb3VudHMubm9ybS5NRFMgPC0KICBjb3VudHMubm9ybSAlPiUKICByZWR1Y2VfZGltZW5zaW9ucyhtZXRob2Q9Ik1EUyIsIC5kaW1zID0gMikKCiMgdGFrZSBhIGxvb2sKY291bnRzLm5vcm0uTURTCmBgYAoKVGhlbiB3ZSBjYW4gc2VsZWN0IGp1c3QgdGhlIGRpbWVuc2lvbnMgZm9yIHRoZSBzYW1wbGVzLgoKYGBge3J9CiMgZ2V0IHRoZSBkaW1lbnNpb25zIHdpdGggYWxsIG1ldGFkYXRhCk1EU2RpbXMgPC0gY291bnRzLm5vcm0uTURTICU+JQpzZWxlY3QoY29udGFpbnMoIkRpbSIpLCBzYW1wbGUsIGltbXVub3BoZW5vdHlwZSwgYGRldmVsb3BtZW50YWwgc3RhZ2VgLCBjb25kaXRpb24pICU+JQpkaXN0aW5jdCgpCgojIHRha2UgYSBsb29rCk1EU2RpbXMKYGBgCgpOZXh0IHdlIGNhbiBwbG90IHRoZSBNRFMgZGltZW5zaW9ucyBhcyBhIHNjYXR0ZXJwbG90LgoKYGBge3J9CiMgTURTIHBsb3QKZ2dwbG90KE1EU2RpbXMsIGFlcyh4PURpbTEsIHk9RGltMiwgY29sb3VyPXNhbXBsZSkpICsgCiAgZ2VvbV9wb2ludCgpCmBgYAoKIyMjIyBFeGVyY2lzZSAKQ29sb3VyIHRoZSBNRFMgcGxvdCB3aXRoIGRpZmZlcmVudCBtZXRhZGF0YSB2YXJpYWJsZXMgZS5nLiBpbW11bm9waGVub3R5cGUuIApTZWUgd2hhdCBgc2hhcGU9YCBkb2VzCkRpc2N1c3Mgd2hhdCBpcyB0aGUgZ3JlYXRlc3Qgc291cmNlIG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YSAoaS5lLiB3aGF0IGRvZXMgZGltZW5zaW9uIDEgcmVwcmVzZW50KT8gV2hhdCBpcyB0aGUgc2Vjb25kIGdyZWF0ZXN0IHNvdXJjZSBvZiB2YXJpYXRpb24gaW4gdGhlIGRhdGE/CgpTb2x1dGlvbgoKYGBge3J9CiMgTURTIHBsb3QgY29sb3VyZWQgYnkgY2VsbCB0eXBlCmdncGxvdChNRFNkaW1zLCBhZXMoeD1EaW0xLCB5PURpbTIsIGNvbG91cj1jb25kaXRpb24pKSArIAogIGdlb21fcG9pbnQoKQpgYGAKCmBgYHtyfQojIE1EUyBwbG90IGNvbG91cmVkIGJ5IGNlbGwgdHlwZQpnZ3Bsb3QoTURTZGltcywgYWVzKHg9RGltMSwgeT1EaW0yLCBjb2xvdXI9aW1tdW5vcGhlbm90eXBlKSkgKyAKICBnZW9tX3BvaW50KCkKYGBgCgpgYGB7cn0KIyBNRFMgcGxvdCBjb2xvdXJlZCBieSBzdGFnZQpnZ3Bsb3QoTURTZGltcywgYWVzKHg9RGltMSwgeT1EaW0yLCBjb2xvdXI9YGRldmVsb3BtZW50YWwgc3RhZ2VgKSkgKyAKICBnZW9tX3BvaW50KCkKYGBgCgpgYGB7cn0KIyBNRFMgcGxvdCBjb2xvdXJlZCBieSBzdGFnZSB3aXRoIHNoYXBlIGZvciBjZWxsIHR5cGUKZ2dwbG90KE1EU2RpbXMsIGFlcyh4PURpbTEsIHk9RGltMiwgY29sb3VyPWBkZXZlbG9wbWVudGFsIHN0YWdlYCwgc2hhcGU9aW1tdW5vcGhlbm90eXBlKSkgKyAKICBnZW9tX3BvaW50KCkKYGBgCgpEZW1vIG1vcmUgTURTIHBsb3RzIChzYW1wbGUgc3dhcCwgYmF0Y2ggZWZmZWN0cykKCiMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24KCk5vdyB0aGF0IHdlIGFyZSBoYXBweSB0aGF0IHdlIGhhdmUgbm9ybWFsaXNlZCB0aGUgZGF0YSBhbmQgdGhhdCB0aGUgcXVhbGl0eSBsb29rcyBnb29kLCB3ZSBjYW4gY29udGludWUgdG8gdGVzdGluZyBmb3IgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLiBXZSB3aWxsIHVzZSB0aGUgYHRlc3RfZGlmZmVyZW50aWFsX2FidW5kYW5jZSgpYCBmb3JtIHR0QnVsayB3aGljaCB1c2VzIGVkZ2VSIHRvIHBlcmZvcm0gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLiBXZSBnaXZlIGB0ZXN0X2RpZmZlcmVudGlhbF9hYnVuZGFuY2UoKWAgYSBmb3JtdWxhIChlLmcuIGAwICsgY29uZGl0aW9uYCksIApzcGVjaWZ5aW5nIHRoZSBjb2x1bW4gdGhhdCBjb250YWlucyBvdXIgZ3JvdXBzIHRvIGJlIGNvbXBhcmVkLiBXZSBjYW4gYWxzbyBwcm92aWRlIHRoZSBuYW1lcyBvZiB0aGUgZ3JvdXBzIHdlIHdhbnQgdG8gY29tcGFyZS9jb250cmFzdCB0byAuY29udHJhc3RzIChlLmcuIC5jb250cmFzdHMgPSBjKCJjb25kaXRpb25icHJlZyAtIGNvbmRpdGlvbmJsYWN0IikpCmBgYHtyfQojIERpZmZlcmVudGlhbCBleHByZXNzaW9uIHdpdGggbGltbWEtdm9vbSAob3IgZWRnZVIpCmNvdW50cy5kZSA8LSBjb3VudHMgJT4lCiAgICB0ZXN0X2RpZmZlcmVudGlhbF9hYnVuZGFuY2UofiAwICsgY29uZGl0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5jb250cmFzdHMgPSBjKCJjb25kaXRpb25icHJlZyAtIGNvbmRpdGlvbmJsYWN0IikpCgojIHRha2UgYSBsb29rCmNvdW50cy5kZQpgYGAKTm93IHdlIGhhdmUgY29sdW1ucyB3aXRoIG91ciBsb2dGQyBhbmQgRkRSIFAgdmFsdWVzLgoKIyMjIyBFeGVyY2lzZQpQZXJmb3JtIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGZvciBscHJlZyB2cyBsbGFjdApQZXJmb3JtIGZvciBicHJlZyB2cyBibGFjdCBhbmQgbHByZWcgdnMgbGxhY3QgYXQgdGhlIHNhbWUgdGltZQoKYGBge3J9CiMgbWF5YmUgaW5jbHVkZQojIHJlbW92ZSBjb25kaXRpb24gZnJvbSBjb2x1bW4gbmFtZXMKIyBjb3VudHMuZGUgPC0gY291bnRzLmRlICU+JSAKIyAgcmVuYW1lX2F0KHZhcnMoY29udGFpbnMoImNvbmRpdGlvbiIpKSwgfiBzdHJfcmVwbGFjZSguLCAiX2NvbmRpdGlvbiIsICIiKSkKYGBgCgpXZSBjYW4gdGFrZSBhIGxvb2sgYXQgdGhlIHRvcCBnZW5lcyBieSBQIHZhbHVlLiBXZSBjYW4gdXNlIGZpbHRlciB0byBzZWxlY3Qgb25seSBnZW5lcyB3aXRoIEZEUiA8IDAuMDUsIHRoZW4gdXNlIGRpc3RpbmN0IHRvIGdldCB0aGUgZGlzdGluY3kgdmFsdWVzIGZvciBjb2x1bW5zIG9mIGludGVyZXN0LCBhbmQgYXJyYW5nZSB0byBzb3J0IGJ5IFBWYWx1ZS4KCmBgYHtyIGV2YWw9RkFMU0V9CmNvdW50cy5kZSAlPiUKICBmaWx0ZXIoRkRSIDwgMC4wNSkgJT4lCiAgZGlzdGluY3QoZ2VuZV9zeW1ib2wsIGxvZ0ZDLCBQVmFsdWUsIEZEUikgJT4lCiAgYXJyYW5nZShQVmFsdWUpCmBgYAoKV2UgY2FuIHdyaXRlIG91dCBvdXIgcmVzdWx0cyB0byBhIGZpbGUgdGhhdCBjYW4gYmUgbG9hZGVkIGludG8gRXhjZWwuCgpgYGB7ciBldmFsPUZBTFNFfQojIHNhdmUgcmVzdWx0cwp3cml0ZV90c3YoY291bnRzLmRlLCAibXlfZGVfcmVzdWx0cy50c3YiKQpgYGAKCiMgUGxvdHMgYWZ0ZXIgdGVzdGluZyBmb3IgREUKCkxldCdzIG1ha2UgYSBmZXcgcGxvdHMgdG8gbWFrZSBzdXJlIGV2ZXJ5dGhpbmcgbG9va3MgZ29vZCBhbmQgdGhhdCB3ZSBoYXZlbid0IG1hZGUgYSBtaXN0YWtlIGluIHRoZSBhbmFseXNpcy4gR2Vub21lLXdpZGUgcGxvdHMgdGhhdCBhcmUgdXNlZnVsIGZvciBjaGVja2luZyBhcmUgTUEgcGxvdHMgYW5kIHZvbGNhbm8gcGxvdHMuIFdlIGNhbiBhbHNvIHVzZSBzdHJpcGNoYXJ0cyBhbmQgaGVhdG1hcHMgdG8gdmlzdWFsaXNlIGdyb3VwcyBvZiBnZW5lcy4KCk1BIHBsb3RzIGVuYWJsZSB1cyB0byB2aXN1YWxpc2UgKiphbW91bnQqKiBvZiBleHByZXNzaW9uIChsb2dDUE0pIHZlcnN1cyBsb2dGQy4gSGlnaGx5IGV4cHJlc3NlZCBnZW5lcyBhcmUgdG93YXJkcyB0aGUgcmlnaHQgb2YgdGhlIHBsb3QuIFdlIGNhbiBhbHNvIGNvbG91ciBzaWduaWZpY2FudCBnZW5lcyAoZS5nLiBnZW5lcyB3aXRoIEZEUiA8IDAuMDUpIAoKYGBge3J9CiMgbWFwbG90LCBtaW5pbWFsCmNvdW50cy5kZSAlPiUKICBmaWx0ZXIoYGZpbHRlciBvdXQgbG93IGNvdW50c2AgPT0gRkFMU0UpICU+JQogIGdncGxvdChhZXMoeD1sb2dDUE0sIHk9LWxvZ0ZDLCBjb2xvdXI9aXNfZGUpKSArCiAgZ2VvbV9wb2ludCgpCiMgY29sb3VyIGJ5IGxvZ2ZjICYgc2V0IHVwIHRvIHJlZCwgZG93biB0byBibHVlCmBgYAoKVm9sY2FubyBwbG90cyBlbmFibGUgdXMgdG8gdmlzdWFsaXNlICoqc2lnbmlmaWNhbmNlKiogb2YgZXhwcmVzc2lvbiAobG9nQ1BNKSB2ZXJzdXMgbG9nRkMuIEhpZ2hseSBzaWduaWZpY2FudCBnZW5lcyBhcmUgdG93YXJkcyB0aGUgdG9wIG9mIHRoZSBwbG90LiBXZSBjYW4gYWxzbyBjb2xvdXIgc2lnbmlmaWNhbnQgZ2VuZXMgKGUuZy4gZ2VuZXMgd2l0aCBGRFIgPCAwLjA1KSAKCmBgYHtyfQojIHZvbGNhbm9wbG90LCBtaW5pbWFsCmNvdW50cy5kZSAlPiUKICBmaWx0ZXIoYGZpbHRlciBvdXQgbG93IGNvdW50c2AgPT0gRkFMU0UpICU+JQogIGdncGxvdChhZXMoeD1sb2dGQywgeT0tbG9nMTAoUFZhbHVlKSwgY29sb3VyPWlzX2RlKSkgKwogIGdlb21fcG9pbnQoKQojIGNvbG91ciBieSBsb2dmYyAmIHNldCB1cCB0byByZWQsIGRvd24gdG8gYmx1ZSwgaGlnaGxpZ2h0IHNvbWUgZ2VuZXMKYGBgCk5lZWQgdG8gam9pbiB0aGUgc2NhbGVkIGNvdW50cyB0byB0aGUgZGUgcmVzdWx0cyB0byBwbG90IG5vcm1hbGlzZWQgY291bnRzIHBlciBnZW5lLgpgYGB7cn0KY291bnRzLmRlIDwtIGZ1bGxfam9pbihjb3VudHMuZGUsIGNvdW50cy5ub3JtKQpgYGAKCmBgYHtyfQojIHN0cmlwY2hhcnRzCmNvdW50cy5kZSAlPiUKCWlubmVyX2pvaW4oICguKSAlPiUgZGlzdGluY3QoZ2VuZV9zeW1ib2wsIFBWYWx1ZSkgJT4lIGFycmFuZ2UoUFZhbHVlKSAlPiUgaGVhZCg2KSkgJT4lCglnZ3Bsb3QoYWVzKHggPSBjb25kaXRpb24sIHkgPSBsb2cyKGBjb3VudCBzY2FsZWRgICsgMSksIGNvbG91ciA9IGNvbmRpdGlvbikpICsKCWdlb21faml0dGVyKCkgKwoJZmFjZXRfd3JhcCh+Z2VuZV9zeW1ib2wpCmBgYAoKYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OH0KIyBoZWF0bWFwIG9mIERFIGdlbmVzCmxpYnJhcnkodGlkeUhlYXRtYXApCgojIGJhc2ljCmNvdW50cy5kZSAlPiUKICBmaWx0ZXIoRkRSIDwgMC4wNSAmIGFicyhsb2dGQykgPiA0KSAlPiUKICBoZWF0bWFwKAogICAgICAgIC5ob3Jpem9udGFsID0gc2FtcGxlLAogICAgICAgIC52ZXJ0aWNhbCA9IGdlbmVfc3ltYm9sLAogICAgICAgIC5hYnVuZGFuY2UgPSBgY291bnQgc2NhbGVkYCwKICAgICAgICBhbm5vdGF0aW9uID0gYyhpbW11bm9waGVub3R5cGUsIGBkZXZlbG9wbWVudGFsIHN0YWdlYCksCiAgICAgICAgbG9nX3RyYW5zZm9ybSA9IFRSVUUKICAgICkKYGBgCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTh9CiMgY3VzdG9taXNlZApjb3VudHMuZGUgJT4lCiAgZmlsdGVyKEZEUiA8IDAuMDUgJiBhYnMobG9nRkMpID4gNCkgJT4lCiAgaGVhdG1hcCgKICAgICAgICAuaG9yaXpvbnRhbCA9IHNhbXBsZSwKICAgICAgICAudmVydGljYWwgPSBnZW5lX3N5bWJvbCwKICAgICAgICAuYWJ1bmRhbmNlID0gYGNvdW50IHNjYWxlZGAsCiAgICAgICAgYW5ub3RhdGlvbiA9IGMoaW1tdW5vcGhlbm90eXBlLCBgZGV2ZWxvcG1lbnRhbCBzdGFnZWApLAogICAgICAgIGxvZ190cmFuc2Zvcm0gPSBUUlVFLAogICAgICAgIHBhbGV0dGVfYWJ1bmRhbmNlID0gYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSwKICAgICAgICBjb2x1bW5fbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gOCkKICAgICkKYGBgCgoK